package com.game.centerserver.controller;

import cn.dev33.satoken.stp.StpUtil;
import com.game.centerserver.common.Result;
import com.game.centerserver.infrastructure.entity.*;
import com.game.centerserver.infrastructure.service.*;
import com.game.centerserver.pojo.request.HttpClientRequest;
import com.game.centerserver.pojo.result.DSTServerProcessView;
import com.game.centerserver.pojo.result.HttpStateCodeResult;
import com.game.centerserver.pojo.result.ServerStateView;
import com.game.centerserver.pojo.result.StartGameResult;
import com.game.centerserver.service.*;
import com.game.centerserver.utils.BaseUtil;
import com.game.centerserver.view.ArchiveView;

import jakarta.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *该控制层用于调用需要与server master进行交互的功能
 * @Author: tian
 * @Date: 2024/10/29/15:41
 * @Description:
 */
@RestController
public class GameServiceController {

    @Resource
    HttpClientService httpClientService;
    @Resource
    GenerateArchive generateArchive;
    @Resource
    ArchiveService archiveService;
    @Resource
    UserServiceService userServiceService;
    @Resource
    OrderServiceService orderServiceService;
    @Resource
    ProductServiceService productServiceService;
    @Resource
    ServerMasterInfoService serverMasterInfoService;
    @Resource
    ErrorMessageService errorMessageService;
    @Resource
    PortService portService;
    @Resource
    DstClusterTokenService dstClusterTokenService;
    @Resource
    DstServerIniService dstServerIniService;
    @Resource
    DstClusterIniService dstClusterIniService;
    @Resource
    ScheduleService scheduleService;

    /**
     * https://***startGame?archiveId=xxx
     * @param
     * @return
     */
    @GetMapping("/startGame")
    public Result<StartGameResult> startGame(Integer productId, String archiveId, boolean withCaves)
    {
        // 根据用户Id和productId查询订单表，判断定单是否到期
        Order order = new Order();
        order.setUid(StpUtil.getLoginIdAsString());
        order.setProductId(productId);
        List<Order> orderList = orderServiceService.query(order);
        if (orderList.size() > 1){
            return Result.fail("该用户有多个重复产品");
        }
        Order targetOrder = orderList.get(0);
        if (targetOrder.getStartTime() == null){
            Order tempOrder = new Order();
            tempOrder.setStartTime(new Date());
            orderServiceService.update(tempOrder);
        }else{
            //判断服务是否到期：当前时间减去start_time是否小于duration
            Date currentData = new Date();
            if((currentData.getTime() - targetOrder.getStartTime().getTime())/(1000 * 60) > targetOrder.getDuration()){
                return Result.fail("产品服务已到期");
            }
        }

        // 判断archive是否属于用户
        User user = userServiceService.queryUserByUid(StpUtil.getLoginIdAsString());
        Archive savedArchive = archiveService.queryById(archiveId);
        if (savedArchive == null) {
            return Result.fail("未查询到存档");
        }
        if (!savedArchive.getUid().equals(StpUtil.getLoginIdAsString())){
            return Result.fail("存档不属于当前用户");
        }

        // 判断archive是否已经在运行/在本地
        Archive archive = archiveService.queryById(archiveId);
        if (archive.getRemote() == 1){
            return Result.fail("游戏服务已经启动，请勿重复启动");
        }

        // 如果在本地
        Product product = productServiceService.queryById(productId);
        // 遍历所有的master，寻找一个满足"product.memsize <= master的可用内存"的master
        String ip = "";
        Integer port = -1;
        List<ServerMasterInfo> serverMasterInfoList = serverMasterInfoService.query(new ServerMasterInfo());
        for (ServerMasterInfo serverMaster : serverMasterInfoList) {
            HttpStateCodeResult<ServerStateView> systemState = httpClientService.getSystemState(serverMaster.getIp(), serverMaster.getPort());
            if (systemState.getCode()==200 &&
                    systemState.getPayload().availableMemory >= 0){
                ip = serverMaster.getIp();
                port = serverMaster.getPort();
                break;
            }
        }

        if (ip == "") {
            System.err.println("未找到合适的服务器");
            return Result.fail("开启游戏失败");
        }

        // 添加cluster_token.txt信息
        // 在token数据库中寻找一个未使用的token
        DstClusterToken findToken = null;
        List<DstClusterToken> dstClusterTokenList = dstClusterTokenService.queryAll();
        for (DstClusterToken dstClusterToken : dstClusterTokenList) {
            if (dstClusterToken.getArchiveId() == null) {
                dstClusterToken.setArchiveId(archiveId);
                dstClusterTokenService.update(dstClusterToken);
                findToken = dstClusterToken;
                break;
            }
        }
        if (findToken == null) {
            System.err.println("archiveId: " + archiveId + " : token find fail.");
            return Result.fail("游戏配置失败");
        }

        // 向Archive, ClusterIni表中插入配置 bindIP是内网ip masterip是公网ip
        ServerMasterInfo serverMasterInfo = serverMasterInfoService.queryById(ip);
        Integer master_port = -1;
        Integer server_portByMaser = -1;
        Integer master_server_portByMaster = -1;
        Integer authentication_portByMaster = -1;
        Integer server_portByCaves = -1;
        Integer master_server_portByCaves = -1;
        Integer authentication_portByCaves = -1;
        Port queryPort = new Port();
        queryPort.setIp(ip);
        queryPort.setUsable(1);
        queryPort.setType(0);
        List<Port> queryres = portService.query(queryPort);
        if (queryres.size() < 7) {
            System.err.println(ip + ": 无可用UDP端口(<7)");
            return Result.fail("开始游戏失败");
        } {
            master_port = queryres.get(0).getPort();
            server_portByMaser = queryres.get(1).getPort();
            master_server_portByMaster = queryres.get(2).getPort();
            authentication_portByMaster = queryres.get(3).getPort();
            server_portByCaves = queryres.get(4).getPort();
            master_server_portByCaves = queryres.get(5).getPort();
            authentication_portByCaves = queryres.get(6).getPort();
        }

        //  配置clusterini和serverini
        DstClusterIni dstClusterIni = dstClusterIniService.queryByArchiveId(archiveId);
        DstServerIni dstServerIniMaster = null;
        DstServerIni dstServerIniCaves = null;
        if (dstClusterIni == null) {
            System.err.println(archiveId + ": archive clusterini配置不存在");
            return Result.fail("配置游戏失败");
        }
        dstClusterIni.setBindIp(serverMasterInfo.getIntranetIp());
        dstClusterIni.setMasterIp(serverMasterInfo.getIntranetIp());
        dstClusterIni.setMasterPort(master_port);
        List<DstServerIni> dstServerInilist = dstServerIniService.queryByArchiveId(archiveId);
        if (dstServerInilist.size() < 2) {
            System.err.println(archiveId + ": archive serverini配置不存在");
            return Result.fail("配置游戏失败");
        } else {
            for (DstServerIni item: dstServerInilist) {
                if (item.isMaster()) {
                    dstServerIniMaster = item;
                } else {
                    dstServerIniCaves = item;
                }
            }
        }
        dstClusterIni.setBindIp(serverMasterInfo.getIntranetIp());
        dstClusterIni.setMasterIp(serverMasterInfo.getIp());
        if (dstServerIniMaster == null || dstServerIniCaves == null) {
            System.err.println(archiveId + ": archive serverini配置不存在");
            return Result.fail("配置游戏失败");
        } else {
            // 配置clusterini
            dstServerIniMaster.setServerPort(server_portByMaser);
            dstServerIniMaster.setMasterServerPort(master_server_portByMaster);
            dstServerIniMaster.setAuthenticationPort(authentication_portByMaster);
            dstServerIniCaves.setServerPort(server_portByCaves);
            dstServerIniCaves.setMasterServerPort(master_server_portByCaves);
            dstServerIniCaves.setAuthenticationPort(authentication_portByCaves);
        }

        boolean res = dstClusterIniService.update(dstClusterIni);
        res &= dstServerIniService.update(dstServerIniMaster);
        res &= dstServerIniService.update(dstServerIniCaves);
        if (!res) {
            System.err.println(archiveId + ": ini配置插入失败");
            return Result.fail("配置游戏失败");
        }

        // 生成（添加）配置文件
        generateArchive.clearConfigFile(new File(archive.getLocalPath()));
        if (!generateArchive.generateConfigsToArchiveDirection(archiveId, new File(archive.getLocalPath())))
        {
            return Result.fail("配置游戏失败");
        };

        //先压缩存档
        File targz = new File("archive/"+StpUtil.getLoginIdAsString()+".tar.gz");
        BaseUtil.compressDirectoryToTarGz(new File(archive.getLocalPath()), targz);
        // 部署archive到master上（根据产品规格输入限制）
        if (ip.length() > 0 && port > 0){
            HttpStateCodeResult<String> buildNewServerResult = httpClientService.buildNewServer(
                    ip,
                    port,
                    product.getMemSize() * 1024,
                    product.getCpuFrequency(),
                    archiveId,
                    targz
            );
            if (buildNewServerResult.getCode() != 200){
                ErrorMessage errorMessage = new ErrorMessage();
                errorMessage.setErrorTime(new Date());
                errorMessage.setMasterIp(ip);
                errorMessage.setMessage(buildNewServerResult.getMessage());
                boolean insert = errorMessageService.insert(errorMessage);
                if (!insert){
                    return Result.fail(ip+"的错误信息持久化失败");
                }
                return Result.fail("部署archive到master上失败");
            }
        }
        // 开启游戏服务
        HttpStateCodeResult<String> startServerByArchiveIDResult = httpClientService.startServerByArchiveID(ip, port, archiveId, "Master");
        if (startServerByArchiveIDResult.getCode() != 200){
            ErrorMessage errorMessage = new ErrorMessage();
            errorMessage.setErrorTime(new Date());
            errorMessage.setMasterIp(ip);
            errorMessage.setMessage(startServerByArchiveIDResult.getMessage());
            boolean insert = errorMessageService.insert(errorMessage);
            if (!insert){
                return Result.fail(ip+"的错误信息持久化失败");
            }
            return Result.fail("启动游戏服务失败");
        }
        if (withCaves) {
            HttpStateCodeResult<String> startCavesServerByArchiveIDResult = httpClientService.startServerByArchiveID(ip, port, archiveId, "Caves");
            if (startCavesServerByArchiveIDResult.getCode() != 200){
                ErrorMessage errorMessage = new ErrorMessage();
                errorMessage.setErrorTime(new Date());
                errorMessage.setMasterIp(ip);
                errorMessage.setMessage(startCavesServerByArchiveIDResult.getMessage());
                boolean insert = errorMessageService.insert(errorMessage);
                if (!insert){
                    return Result.fail(ip+"的错误信息持久化失败");
                }
                return Result.fail("启动游戏服务失败");
            }
        }

        //将archive的remote的值设置为1
        Archive updateArchive = new Archive();
        updateArchive.setArchiveId(archiveId);
        updateArchive.setRemote(1);
        updateArchive.setMasterIp(ip);
        boolean update = archiveService.update(updateArchive);
        if (!update){
            return Result.fail("未能将archive的remote的值设置为1");
        }

        // 删除本地存档
        BaseUtil.deleteDirectory(new File(archive.getLocalPath()));

        StartGameResult startGameResult = new StartGameResult(
                ip,
                master_port
        );

        return Result.ok("启动游戏服务成功", startGameResult);
    }

    @GetMapping("/stopGame")
    public Result stopGame(String archiveId) {
        // 判断archive是否属于用户
        User user = userServiceService.queryUserByUid(StpUtil.getLoginIdAsString());
        Archive archive = archiveService.queryById(archiveId);
        if (archive == null) {
            return Result.fail("存档不存在");
        }
        if (!user.getUid().equals(archive.getUid())){
            return Result.fail("存档不属于当前用户");
        }
        // 判断archive是否已经在运行/在本地
        if (archive.getRemote() == 0){
            return Result.fail("游戏服务未启动，不用停止服务");
        }

        // 如果在远端，关闭进程服务
        ServerMasterInfo serverMasterInfo = serverMasterInfoService.queryById(archive.getMasterIp());
        HttpStateCodeResult<String> shutdownServerResult = httpClientService.shutdownServer(archive.getMasterIp(), serverMasterInfo.getPort(), archiveId);
        if (shutdownServerResult.getCode() != 200){
            ErrorMessage errorMessage = new ErrorMessage();
            errorMessage.setErrorTime(new Date());
            errorMessage.setMasterIp(archive.getMasterIp());
            errorMessage.setMessage(shutdownServerResult.getMessage());
            boolean insert = errorMessageService.insert(errorMessage);
            if (!insert){
                return Result.fail(archive.getMasterIp()+"的错误信息持久化失败");
            }
            return Result.fail("关闭游戏服务失败");
        }
        // 召回存档
        boolean res = scheduleService.callBackArchiveByIdAndSave(serverMasterInfo.getIp(), serverMasterInfo.getPort(), archiveId);
        if (!res) {
            System.err.println("archiveID: " + archiveId + " 存档召回失败.");
            return Result.fail("关闭游戏服务失败");
        }

        return Result.ok("成功召回存档");
    }

    @GetMapping("/getServerProcessState")
    public Result getServerProcessState(String archiveId)
    {
        // 判断archive是否属于用户
        User user = userServiceService.queryUserByUid(StpUtil.getLoginIdAsString());
        String[] archiveIdList = user.getArchiveId().split(",");
        boolean flag = false;
        for (String id : archiveIdList) {
            if (id.equals(archiveId)){
                flag = true;
                break;
            }
        }
        if (!flag){
            return Result.fail("存档不属于当前用户");
        }
        // 判断archive是否已经在运行
        Archive archive = archiveService.queryById(archiveId);
        if (archive.getRemote() == 0){
            return Result.fail("游戏服务未启动");
        }
        // 如果在远端，直接调用httpclientservice.getServerStateList
        ServerMasterInfo serverMasterInfo = serverMasterInfoService.queryById(archive.getMasterIp());
        HttpStateCodeResult<List<DSTServerProcessView>> getServerStateListResult = httpClientService.getServerStateList(archive.getMasterIp(), serverMasterInfo.getPort());
        if (getServerStateListResult.getCode() != 200){
            ErrorMessage errorMessage = new ErrorMessage();
            errorMessage.setErrorTime(new Date());
            errorMessage.setMasterIp(archive.getMasterIp());
            errorMessage.setMessage(getServerStateListResult.getMessage());
            boolean insert = errorMessageService.insert(errorMessage);
            if (!insert){
                return Result.fail(archive.getMasterIp()+"的错误信息持久化失败");
            }
            return Result.fail("获取游戏服务服务器信息失败");
        }
        // 返回archiveId指定的进程服务的状态
        for (DSTServerProcessView dstServerProcessView : getServerStateListResult.getPayload()) {
            if (dstServerProcessView.getArchiveID().equals(archiveId)){
                return Result.ok("获取游戏服务服务器信息成功",dstServerProcessView);
            }
        }

        return Result.fail("获取"+archiveId+"所对应的游戏服务服务器信息失败");
    }

    @GetMapping("/console")
    public Result console(String archiveId, String cmd)
    {
        // 判断archive是否属于用户
        User user = userServiceService.queryUserByUid(StpUtil.getLoginIdAsString());
        String[] archiveIdList = user.getArchiveId().split(",");
        boolean flag = false;
        for (String id : archiveIdList) {
            if (id.equals(archiveId)){
                flag = true;
                break;
            }
        }
        if (!flag){
            return Result.fail("存档不属于当前用户");
        }
        // 判断archive是否已经在运行
        Archive archive = archiveService.queryById(archiveId);
        if (archive.getRemote() == 0){
            return Result.fail("游戏服务未启动");
        }

        // 如果在运行，直接调用httpclientservice.console的指定接口

        return Result.ok("成功调用httpclientservice.console");
    }
}
